package com.etoak.stock.logs;

import com.etoak.stock.annoation.MyLog;
import com.etoak.stock.pojo.entity.SysLog;
import com.etoak.stock.service.LogService;
import com.etoak.stock.utils.IdWorker;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.io.BaseEncoding;
import org.aopalliance.intercept.Joinpoint;
import org.apache.poi.hssf.record.DVALRecord;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.weaver.ast.Var;
import org.aspectj.lang.JoinPoint;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @Author by 孙有泽
 * @Date 2024/11/10 14:24
 * @Description
 */
@Aspect
public class OperLogAspect {
    @Autowired
    private LogService logService;

    @Autowired
    private IdWorker idWorker;
    ThreadLocal<Long> startTime=new ThreadLocal<>();
    /*
     * @author 孙有泽
     * @date 2024/11/10 14:29
     * @description 设置操作日志切入点 这里介绍二中方式
     * 1.基于注解切入 这里打了自定义注解的方法才会切入
     * @Pointcut("@annotation(com.etoak.stock.logs.OperLog)")
     * 2.集于包扫描切入
     * @Pointcut("execution(* com.etoak.stock.controller..*.*(..))")
     */
    @Pointcut("@annotation(com.etoak.stock.annoation.MyLog)")
    //@Pointcut("@execution(* com.etoak.stock.controller..*.*(..))")
    public void operLogPointCut() {

    }


    @Before("operLogPointCut()")
    public void beforeMethod(JoinPoint point){
        startTime.set(System.currentTimeMillis());
    }
    /*
     * @author 孙有泽
     * @date 2024/11/10 14:57
     * @description 设置操作异常切入点记录异常日志 扫描所有controller包下的操作
     */
    @Pointcut("execution(* com.etoak.stock.controller..*.*(..))")
    public void operExceptionLogPointCut() {

    }

    /*
     * @author 孙有泽
     * @date 2024/11/10 14:59
     * @description 正常返回通知，拦截用户操作日志 连接点正常执行完成后执行 如果连接点抛出异常 则不会执行
     * joinPoint 获取连接点信息
     * result 获取返回值
     */

    @AfterReturning(value = "operLogPointCut()",returning = "result")
    public void saveLog(JoinPoint joinPoint, Object result){
        //获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request=  (HttpServletRequest)requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        //从切面织入点出通过反射的机制获取织入点出的方法
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //获取切入点的方法
        Method method = signature.getMethod();
        MyLog annotation = method.getAnnotation(MyLog.class);

        SysLog sysLog = new SysLog();
        sysLog.setId(idWorker.nextId());
        String authorization = request.getHeader("Authorization");
        byte[] decode = BaseEncoding.base64().decode(authorization);
        String s = new String(decode);
        sysLog.setUserId(s.split(":")[0]);
        sysLog.setUsername(s.split(":")[1]);
        if(annotation!=null){
            sysLog.setOperation(annotation.value());
        }
        Long takeTime=System.currentTimeMillis()-startTime.get();//记录方法执行消耗的时间
        //获取接口和类全限定名
        String declaringTypeName = signature.getDeclaringTypeName();
        //获取方法名字
        String methodName = signature.getName();
        sysLog.setMethod(declaringTypeName+"."+methodName);
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            sysLog.setParams(objectMapper.writeValueAsString(joinPoint.getArgs()));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
        sysLog.setIp(getIp(request));
        logService.addLog(sysLog);

    }

    //根据HttpServletRequest获取访问者的IP地址
    public static String getIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }


}
